/*
* Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).
* All rights reserved.
* This component and the accompanying materials are made available
* under the terms of "Eclipse Public License v1.0"
* which accompanies this distribution, and is available
* at the URL "http://www.eclipse.org/legal/epl-v10.html".
*
* Initial Contributors:
* Nokia Corporation - initial contribution.
*
* Contributors:
*
* Description:
*/

#include <s32mem.h> // RDesReadStream.

#include "sharedmemory.h"
#include "sharedmemory_symbian_p.h"

_LIT( KSemaphoreName, "nfccontenthandlersemaphore" );
_LIT( KMutexName, "nfccontenthandlermutex" );
_LIT( KChunkName, "nfccontenthandlerchunk" );

const TInt KChunkSize = 1024;

const TInt KSharedMemoryHeaderSize = sizeof( TInt64 ) + sizeof( TInt );

SharedMemoryPrivate::SharedMemoryPrivate( SharedMemory *q ) :
    CBase(),
    q_ptr( q )
    {
    OpenMutex();
    OpenChunk();
    }

SharedMemoryPrivate::~SharedMemoryPrivate()
    {
    delete iMessage;

    iMutex.Close();
    iChunk.Close();
    }

bool SharedMemoryPrivate::isModified()
    {
    Q_Q(SharedMemory);

    bool isModified = EFalse;

    TRAPD( error, ReadSharedMemoryHeaderL() )

    if ( error == KErrNone )
        {
        isModified = iMessageTime != iHeaderTime;
        }
    else
        {
        emit q->error( error );
        }

    return isModified;
    }

QByteArray SharedMemoryPrivate::message()
    {
    Q_Q(SharedMemory);

    QByteArray message;

    TRAPD( error, ReadSharedMemoryL() )

    if ( error == KErrNone )
        {
        message = QByteArray::fromRawData( reinterpret_cast<const char *>((*iMessage).Ptr()), iHeaderMessageSize );
        }
    else
        {
        emit q->error( error );
        }

    return message;
    }

void SharedMemoryPrivate::CleanupSignal( TAny* aAny )
    {
    if ( aAny )
        {
        RMutex* mutex = reinterpret_cast<RMutex*>( aAny );
        mutex->Signal();
        }
    }

void SharedMemoryPrivate::OpenMutex()
    {
    iMutexError = iMutex.OpenGlobal( KMutexName );

    if ( iMutexError != KErrNone )
        {
        iMutexError = iMutex.CreateGlobal( KMutexName );
        }
    }

void SharedMemoryPrivate::OpenChunk()
    {
    iChunkError = iChunk.OpenGlobal( KChunkName, ETrue );

    if ( iChunkError != KErrNone )
        {
        iChunkError = iChunk.CreateGlobal( KChunkName, KChunkSize, KChunkSize );

        if ( iChunkError == KErrNone )
            {
            TPtr8 chunkPtr( iChunk.Base(), iChunk.Size() );
            chunkPtr.FillZ( chunkPtr.MaxSize() );
            }
        }
    else
        {
        SignalSemaphore();
        }
    }

void SharedMemoryPrivate::SignalSemaphore()
    {
    if ( (iMutexError == KErrNone) && (iChunkError == KErrNone) )
        {
        RSemaphore semaphore;

        if ( semaphore.OpenGlobal(KSemaphoreName) == KErrNone )
            {
            semaphore.Signal();
            semaphore.Close();
            }
        }
    }

void SharedMemoryPrivate::ReadSharedMemoryL()
    {
    ReadSharedMemoryHeaderL();
    ReadSharedMemoryMessageL();
    }

void SharedMemoryPrivate::ReadSharedMemoryHeaderL()
    {
    iHeaderTime = 0;
    iHeaderMessageSize = 0;

    if ( (iMutexError == KErrNone) && (iChunkError == KErrNone) )
        {
        iMutex.Wait();
        CleanupStack::PushL( TCleanupItem(&SharedMemoryPrivate::CleanupSignal, reinterpret_cast<TAny*>(&iMutex)) );

        const TInt chunkSize = iChunk.Size();
        const TPtrC8 chunkPtr( iChunk.Base(), chunkSize );

        if ( KSharedMemoryHeaderSize <= chunkSize )
            {
            RDesReadStream readStream( chunkPtr );
            CleanupClosePushL( readStream );

            const TUint32 headerTimeHigh = readStream.ReadUint32L();
            const TUint32 headerTimeLow = readStream.ReadUint32L();

            iHeaderTime = MAKE_TINT64( headerTimeHigh, headerTimeLow );
            iHeaderMessageSize = readStream.ReadInt32L();

            CleanupStack::PopAndDestroy( &readStream );
            }
        else
            {
            User::Leave( KErrCorrupt );
            }

        CleanupStack::PopAndDestroy(); // The TCleanupItem cleanup operation will call iMutex.Signal().
        }
    else
        {
        User::Leave( KErrNotReady );
        }
    }

void SharedMemoryPrivate::ReadSharedMemoryMessageL()
    {
    delete iMessage;
    iMessage = NULL;

    if ( (iMutexError == KErrNone) && (iChunkError == KErrNone) )
        {
        iMutex.Wait();
        CleanupStack::PushL( TCleanupItem(&SharedMemoryPrivate::CleanupSignal, reinterpret_cast<TAny*>(&iMutex)) );

        const TInt chunkSize = iChunk.Size();

        if ( iHeaderMessageSize <= (chunkSize - KSharedMemoryHeaderSize) )
            {
            const TPtrC8 chunkPtr( iChunk.Base() + KSharedMemoryHeaderSize, iHeaderMessageSize );

            iMessage = chunkPtr.AllocL();
            iMessageTime = iHeaderTime;
            }
        else
            {
            User::Leave( KErrCorrupt );
            }

        CleanupStack::PopAndDestroy(); // The TCleanupItem cleanup operation will call iMutex.Signal().
        }
    else
        {
        User::Leave( KErrNotReady );
        }
    }
